﻿/*
 *   Общая идея.
 *  Я предусматриваю существование нескольких движков для работы с единицами.
 *  Какой бы ни была система, она должна поддердивать две операции. 
 *  По сути, это одна и та же операция
 *   - проверка связности двух единиц
 *   - проверка связности  + преобразование значения
 *   
 *  Единица (IUnit) хранит данные, деобходимые движку, чтобы выполнять операции.
 */

using System;
using System.Collections.Generic;

namespace Novice.Units
{
    /// <summary>
    /// Единица измерения хранит данные, необходимые, чтобы выполнять операции.
    /// </summary>
    /// <remarks>
    /// IUnit - "черный ящик".
    /// Важно не что в нем находится, а что можно с этим сделать
    /// </remarks>
    public interface IUnit
    {
        /// <summary>
        /// Название единицы
        /// </summary>
        /// <remarks>
        /// необходимость этого поля сомнительна.
        /// </remarks>
        string Name { get; }
    }

    /// <summary>
    /// Алгебра единиц инкапсулирует операции над единицами
    /// </summary>
    /// <remarks>
    /// Другое название IUnitService.
    /// Я назвал его алгеброй, т.к. этот интерфейс моделирует математическое понятие алгебры
    /// </remarks>
    public interface IUnitAlgebra
    {
        /// <summary>
        /// Проверка, применима ли данная алгебра к данной единице
        /// </summary>
        /// <param name="unit">проверяемя единица</param>
        /// <returns>возвращает true, если алгебра применима к единице</returns>
        /// <remarks>
        /// Допустимо использовать одновременно несколько движков, реализующих IUnitAlgebra
        /// Может получиться так, что движку передается единица, взятая из другого движка, или просто несовместимая
        /// Эта операция проверяет, может ли движок работать с данной единицей
        /// </remarks>
        bool CanWorkWith(IUnit unit);

        /// <summary>
        /// Единица, которую имеет просто число без единиц.
        /// </summary>
        //IUnit ZeroUnit { get; }

        /// <summary>
        /// Преобразование значения из одной единицы в другую
        /// </summary>
        /// <param name="valueFrom">значение</param>
        /// <param name="unitFrom">исходная единица</param>
        /// <param name="unitTo">конечная единица</param>
        /// <param name="valueTo">конечное значение</param>
        /// <returns>возвращает true, если преобразование возможно</returns>
        /// <remarks>
        /// Преобразование считается возможным, если единицы принадлежат одному классу эквивалентности. Иначе - невозможно
        /// Остальные ошибки в расчет не принимаются. Например, деление на ноль и т.п.
        /// </remarks>
        bool Convert(double valueFrom, IUnit unitFrom, IUnit unitTo, out double valueTo);

        /// <summary>
        /// Умножение единицы на единицу
        /// </summary>
        /// <param name="left">левый множитель-единица</param>
        /// <param name="right">правый множитель-единица</param>
        /// <param name="value">значение </param>
        /// <returns></returns>
        /// <remarks>
        /// Например 
        /// 1м*1км = 1000м2
        /// 1(С)*1(F)
        /// </remarks>
        //IUnit Multiply(IUnit left, IUnit right, double value);
        //IUnit Divide(IUnit left, IUnit right);
        bool CheckEquivalence(IUnit left, IUnit right);
    }

    /// <summary>
    /// Исключение при работе с единицами измерения
    /// </summary>
    public class UnitException: ApplicationException
    {
        public UnitException()
        {}

        public UnitException(string message)
            :base(message)
        {}

        public UnitException(string message, Exception innerException)
            :base(message, innerException)
        {}
    }

    /// <summary>
    /// Попытка выполнения операции с единицами, принадлежащими к разным движкам
    /// </summary>
    public class UnitAlgebraMismatchException: UnitException
    {}

    /// <summary>
    /// Попытка выполнения операции с несовместимыми единицами
    /// </summary>
    public class NonequivalentUnitsException : UnitException
    { }

    /// <summary>
    /// Единица не совместима с данной алгеброй
    /// </summary>
    public class UnsupportedUnitException : UnitException
    { }


}
